{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Part 2, Topic 1: Introduction to Voltage Glitching (MAIN)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "NOTE: This lab references some (commercial) training material on [ChipWhisperer.io](https://www.ChipWhisperer.io). You can freely execute and use the lab per the open-source license (including using it in your own courses if you distribute similarly), but you must maintain notice about this source location. Consider joining our training course to enjoy the full experience.\n",
    "\n",
    "---"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**SUMMARY:** *Similarly to clock glitching, inserting brief glitches into the power line of an embedded device can result in skipped instructions and corrupted results. Besides providing a more reliable glitch on some targets when compared to clock glitching, voltage glitching also has the advanatage that the Vcc pins on chips are more commonly accessable. This won't be covered in this course, but it can also be used to glitch a device asynchronous to its clock.*\n",
    "\n",
    "**LEARNING OUTCOMES:**\n",
    "\n",
    "* Understanding voltage glitch settings\n",
    "* Building a voltage glitch and crash map.\n",
    "* Modifying glitch circuit to increase glitch success"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Voltage Glitch Hardware\n",
    "\n",
    "The ChipWhisperer uses the same hardware block for both voltage and clock glitching, with the only difference being where the glitch output is routed to. Instead of routing to HS2, voltage glitching is performed by routing the glitch to either the `glitch_hp` transistor or the `glitch_lp` transistor. This can be done via the following API calls:\n",
    "\n",
    "```python\n",
    "scope.io.glitch_hp = True #enable HP glitch\n",
    "scope.io.glitch_hp = False #disable LP glitch\n",
    "scope.io.glitch_lp = True #enable LP glitch\n",
    "scope.io.glitch_lp = False #disable LP glitch\n",
    "```\n",
    "\n",
    "While the hardware block are the same, you'll need to change how it's configued. You wouldn't want to try routing `\"clock_xor\"` to the glitch transistor and oscillate Vcc like the device's clock! Instead, the following two output settings are best suited to voltage glitching:\n",
    "\n",
    "1. `\"glitch_only\"` - insert a glitch for a portion of a clock cycle based on `scope.glitch.width` and `scope.glitch.offset`\n",
    "1. `\"enable_only\"` - insert a glitch for an entire clock cycle\n",
    "\n",
    "Typically, the `\"enable_only\"` setting will be too powerful for most devices. One situation where it outshines `\"glitch_only\"` is in glitching asychronous to the target's clock. An example of this is glitching a target with an internal clock. In this case, the ChipWhisperer's clock can be boosted far above the target's to insert a precise glitch, with `repeat` functioning as `width` and `ext_offset` functioning as `offset`.\n",
    "\n",
    "### Voltage Glitching vs. Clock Glitching\n",
    "\n",
    "Voltage glitching has some obvious benefits over clock glitching, such as working for a wider varitey of targets, but its downsides are less obvious. One of the biggest is how much it depends on the actual glitch circuit itself. With clock glitching, it's relatively easy to insert a glitch - there's nothing external trying to keep the clock at a certain voltage level. This is very different for a target's power pins. When we try to drop the power pin to ground, there's a lot of stuff fighting us to keep the power pin at the correct voltage, such as decoupling capacitors, bulk supply capacitors, and the voltage regulator supplying the voltage. This means when we make small changes to the glitch circuit, the glitch settings and even our ability to insert a glitch at all completely change! Consider glitching a target on the CW308 UFO board. If you switch your coaxial cable length from 20cm to 40 cm, you'll need to find entirely new glitch settings to repeat the attack (if it's still even possible). This is quite easy to see on an oscilloscope or  using the ChipWhisperer's ADC: longer cables and lower valued shunt resistors will make the glitch less sharp and increase ringing.\n",
    "\n",
    "While your first thought might be to go for as sharp a glitch as possible, this often won't result in a high glitch success rate. If you're unable to find any working glitches with your current setup, it might be worth changing you hardware setup a bit. For example, on the ChipWhisperer Lite 1 part, you can desolder SJ5 and solder header pins to JP6. Even just connecting these pins with a jumper will have different glitch behaviour than with a soldered SJ5.\n",
    "\n",
    "You can refer to the training slides for more information about finding good glitch settings, as well as more on the theory side of voltage glitching.\n",
    "\n",
    "### The Lab\n",
    "\n",
    "To introduce you to volatge glitching and find some settings, we're going to walk back through the clock glitching loop lab. You may want to capture some power traces while you're first experimenting with glitches to see what effects different glitch widths have on the power trace. Another thing to keep in mind is that targets often won't tolerate the Vcc pin dropping for an extended period of time without crashing - once you see the target start to crash, you won't see much else with larger widths.\n",
    "\n",
    "One thing you might have to change is the glitch repeat value. Depending on how wide your glitch is, the voltage at the power pin may not recover by the time the next glitch is inserted. This can have to effect of increasing subsequent glitches' strength, which may or may not be desirable. Since glitches inserted with repeat > 1 have different strength, it's a good idea to scan through ext_offset as well.\n",
    "\n",
    "###  Higher Frequency Glitching\n",
    "\n",
    "The XMEGA target, and to a lesser extent the STM32F3, is very difficult to glitch with the default ChipWhisperer settings. Try bumping the clock frequency to 24MHz for the STM32 or 32MHz for the XMEGA and use a repeat 5-10 with both the high power and low power glitches active. You'll need to adjust the baud rate by the same proportion as the clock. This will increase the glitch precision, as the glitch width/offset step is based on the target clock, and may decrease the stability of the target.\n",
    "\n",
    "Another setup that seems to work with the XMEGA is SJ5 unsoldered, JP6 jumpered, high+low power glitch, 32MHz, and repeat=5.\n",
    "\n",
    "The ChipWhisperer Husky, with its PLL based glitching system, can typically glitch targets without needing to change the target's frequency.\n",
    "\n",
    "### Disabling Logging\n",
    "\n",
    "When glitching (or just running normally in earlier labs), you may have seen various warnings from loggers ChipWhisperer uses. This often has useful information, especially if things don't work right, but for voltage \n",
    "glitching especially, it mostly clutters up any print output you have. As such, we'll disable logging for the voltage glitching labs:\n",
    "\n",
    "```python\n",
    "cw.set_all_log_levels(cw.logging.CRITICAL)\n",
    "```\n",
    "\n",
    "You can reenable logging via\n",
    "\n",
    "```python\n",
    "cw.set_all_log_levels(cw.logging.WARNING)\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "SCOPETYPE = 'OPENADC'\n",
    "PLATFORM = 'CWLITEARM'\n",
    "SS_VER = 'SS_VER_2_1'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%run \"../../Setup_Scripts/Setup_Generic.ipynb\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%bash -s \"$PLATFORM\" \"$SS_VER\"\n",
    "cd ../../../firmware/mcu/simpleserial-glitch\n",
    "make PLATFORM=$1 CRYPTO_TARGET=NONE SS_VER=$2 -j"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "fw_path = \"../../../firmware/mcu/simpleserial-glitch/simpleserial-glitch-{}.hex\".format(PLATFORM)\n",
    "cw.program_target(scope, prog, fw_path)\n",
    "if SS_VER==\"SS_VER_2_1\":\n",
    "    target.reset_comms()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def reboot_flush():\n",
    "    reset_target(scope)\n",
    "    target.flush()\n",
    "\n",
    "if scope._is_husky is False:\n",
    "    if PLATFORM == \"CWLITEXMEGA\":\n",
    "        scope.clock.clkgen_freq = 32E6\n",
    "        if SS_VER=='SS_VER_2_1':\n",
    "            target.baud = 230400*32/7.37\n",
    "        else:\n",
    "            target.baud = 38400*32/7.37\n",
    "    elif (PLATFORM == \"CWLITEARM\") or (\"F3\" in PLATFORM):\n",
    "        scope.clock.clkgen_freq = 24E6\n",
    "        if SS_VER=='SS_VER_2_1':\n",
    "            target.baud = 230400*24/7.37\n",
    "        else:\n",
    "            target.baud = 38400*24/7.37\n",
    "        time.sleep(0.1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "reboot_flush()\n",
    "scope.arm()\n",
    "target.simpleserial_write(\"g\", bytearray([]))\n",
    "scope.capture()\n",
    "val = target.simpleserial_read_witherrors('r', 4, glitch_timeout=10)#For loop check\n",
    "valid = val['valid']\n",
    "if valid:\n",
    "    response = val['payload']\n",
    "    raw_serial = val['full_response']\n",
    "    error_code = val['rv']\n",
    "\n",
    "print(val)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "gc = cw.GlitchController(groups=[\"success\", \"reset\", \"normal\"], parameters=[\"width\", \"offset\", \"ext_offset\", \"tries\"])\n",
    "gc.display_stats()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "gc.glitch_plot(plotdots={\"success\":\"+g\", \"reset\":\"xr\", \"normal\":None})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Next, we'll setup the glitch. The main differences here compared to clock glitching is the use of `scope.io.glitch_lp/hp`, which activates the transistors used for voltage, and `scope.glitch.output = 'glitch_only'`, which sends the glitch output to the transistors instead of xoring it with the clock like with clock glitching."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "if scope._is_husky:\n",
    "    scope.glitch.enabled = True\n",
    "    scope.glitch.clk_src = \"pll\"\n",
    "    scope.io.glitch_hp = False\n",
    "    scope.io.glitch_hp = True\n",
    "    scope.io.glitch_lp = False\n",
    "    scope.io.glitch_lp = False\n",
    "else:\n",
    "    scope.glitch.clk_src = \"clkgen\" # set glitch input clock\n",
    "scope.glitch.output = \"glitch_only\" # glitch_out = clk ^ glitch\n",
    "scope.glitch.trigger_src = \"ext_single\" # glitch only after scope.arm() called\n",
    "if PLATFORM == \"CWLITEXMEGA\":\n",
    "    scope.io.glitch_lp = True\n",
    "    scope.io.glitch_hp = True\n",
    "elif PLATFORM == \"CWLITEARM\":\n",
    "    scope.io.glitch_lp = True\n",
    "    scope.io.glitch_hp = True\n",
    "elif PLATFORM == \"CW308_STM32F3\":\n",
    "    scope.io.glitch_hp = True\n",
    "    scope.io.glitch_lp = True"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Some tips for finding good glitches:\n",
    "\n",
    "1. There's a lot of stuff fighting our glitch this time - unlike the clock line, the Vcc rail isn't supposed to oscillate! As such shorter glitches will have no effect. Often, good widths will be just below when the target starts consistantly crashing, so a good strategy is to find the minimum width where crashes always happen, then backing the width off a bit.\n",
    "1. The repeat parameter behaves very differently than with voltage glitching - at the boosted clock rate, the Vcc often won't recover before the next glitch. Try different repeat values as well.\n",
    "1. We've built in a success/reset measurement into the glitch loop. Once you've found some glitch spots, this will help you evaluate which ones are best for your target.\n",
    "\n",
    "It can take a very long time to do go through the full search space, so you may want to stop after you get a certain number of succeses. By default here, it will be 1, but you may want to change it to 10, 20, or go even higher."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "MAX_SUCCESSES = 1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import struct\n",
    "\n",
    "#disable logging\n",
    "cw.set_all_log_levels(cw.logging.CRITICAL)\n",
    "\n",
    "# These width/offset settings are for CW-Lite/Pro; width/offset are expressed differently for Husky (see Fault 1_1)\n",
    "num_tries = 5\n",
    "\n",
    "gc.set_range(\"tries\", 0, num_tries)\n",
    "gc.set_range(\"width\", ???, ???)\n",
    "gc.set_range(\"offset\", ???, ???)\n",
    "gc.set_global_step(???)\n",
    "gc.set_step(\"tries\", 1)\n",
    "gc.set_step(\"ext_offset\", 1)\n",
    "\n",
    "\n",
    "scope.adc.timeout = 0.5\n",
    "\n",
    "reboot_flush()\n",
    "loff = scope.glitch.offset\n",
    "lwid = scope.glitch.width\n",
    "total_successes = 0\n",
    "successes = 0\n",
    "resets = 0\n",
    "for glitch_setting in gc.glitch_values():\n",
    "    scope.glitch.offset = glitch_setting[1]\n",
    "    scope.glitch.width = glitch_setting[0]\n",
    "    scope.glitch.ext_offset = glitch_setting[2]\n",
    "    if glitch_setting[3] == 0:\n",
    "        total_successes += successes\n",
    "        successes = 0\n",
    "        resets = 0\n",
    "        if total_successes > MAX_SUCCESSES:\n",
    "            break\n",
    "    target.flush()\n",
    "    if scope.adc.state:\n",
    "        # can detect crash here (fast) before timing out (slow)\n",
    "        #print(\"Trigger still high!\")\n",
    "        gc.add(\"reset\")\n",
    "\n",
    "        #Device is slow to boot?\n",
    "        reboot_flush()\n",
    "        resets += 1\n",
    "\n",
    "    scope.arm()\n",
    "\n",
    "    #Do glitch loop\n",
    "    target.simpleserial_write(\"g\", bytearray([]))\n",
    "\n",
    "    ret = scope.capture()\n",
    "\n",
    "    scope.io.vglitch_reset()\n",
    "    if ret:\n",
    "        #print('Timeout - no trigger')\n",
    "        gc.add(\"reset\")\n",
    "        resets += 1\n",
    "\n",
    "        #Device is slow to boot?\n",
    "        reboot_flush()\n",
    "\n",
    "    else:\n",
    "        val = target.simpleserial_read_witherrors('r', 4, glitch_timeout=10, timeout=50)#For loop check\n",
    "        if ???:\n",
    "            gc.add(\"reset\")\n",
    "            reboot_flush()\n",
    "            resets += 1\n",
    "            #print(val)\n",
    "        else:\n",
    "            gcnt = struct.unpack(\"<I\", val['payload'])[0]\n",
    "\n",
    "            if gcnt != 2500: #for loop check\n",
    "                gc.add(\"success\")\n",
    "                print((scope.glitch.width, scope.glitch.offset, scope.glitch.ext_offset))\n",
    "                successes += 1\n",
    "            else:\n",
    "                gc.add(\"normal\")\n",
    "    if (successes > 0):\n",
    "        print(\"successes = {}, resets = {}, offset = {}, width = {}, ext_offset = {}\".format(successes, resets, scope.glitch.offset, scope.glitch.width, scope.glitch.ext_offset))\n",
    "        total_successes += successes\n",
    "print(\"Done glitching\")\n",
    "#enable logging\n",
    "cw.set_all_log_levels(cw.logging.WARNING)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "With that done, let's print our results. You'll likely want to ignore the \"tries\" and \"ext_offset\" parameters:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "results = gc.calc(ignore_params=[\"tries\", \"ext_offset\"], sort=\"success_rate\")\n",
    "results"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "scope.dis()\n",
    "target.dis()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
